﻿using System;
using System.Collections.Generic;
using System.Linq;
using Airborne.ScrumPoker.Model.TeamFoundation;
using Airborne.ScrumPoker.Module.Events;
using Airborne.ScrumPoker.Module.Host.Model;

namespace Airborne.ScrumPoker.Module.Host.Services
{
    public class ConnectionManager
    {

        #region ConnectionRegister Singleton
        private static ConnectionManager connectionRegister;
        private static object mutex = new object();

        /// <summary>
        /// Gets a thread safe single instance of <see cref="ConnectionRegister"/>
        /// </summary>
        public static ConnectionManager Instance
        {
            get
            {
                lock (mutex)
                {
                    if (connectionRegister == null)
                    {
                        connectionRegister = new ConnectionManager();
                    }
                }
                return connectionRegister;
            }
        }
        #endregion

        #region Events

        #region ClientVote

        public event EventHandler<ClientVotedEventArgs> ClientVote;

        private void RaiseClientVote(string developer, double vote)
        {
            ClientVotedEventArgs args = new ClientVotedEventArgs(developer, vote);
            OnClientVote(args);
        }

        protected virtual void OnClientVote(ClientVotedEventArgs args)
        {
            EventHandler<ClientVotedEventArgs> handler = this.ClientVote;
            if (handler != null)
            {
                handler(this, args);
            }
        }

        #endregion

        #region ClientSubscribed

        public event EventHandler<GenericEventArgs<string>> ClientSubscribed;

        private void RaiseClientSubscribed(string client)
        {
            GenericEventArgs<string> args = new GenericEventArgs<string>(client);
            OnClientSubscribed(args);
        }

        protected virtual void OnClientSubscribed(GenericEventArgs<string> args)
        {
            EventHandler<GenericEventArgs<string>> handler = this.ClientSubscribed;
            if (handler != null)
            {
                handler(this, args);
            }
        }

        #endregion

        #region ClientUnsubscribed

        public event EventHandler<GenericEventArgs<string>> ClientUnsubscribed;

        private void RaiseClientUnsubscribed(string client)
        {
            GenericEventArgs<string> args = new GenericEventArgs<string>(client);
            OnClientUnsubscribed(args);
        }

        protected virtual void OnClientUnsubscribed(GenericEventArgs<string> args)
        {
            EventHandler<GenericEventArgs<string>> handler = this.ClientUnsubscribed;
            if (handler != null)
            {
                handler(this, args);
            }
        }

        #endregion
        #endregion

        #region Fields

        private IList<IScrumPokerClientConnection> connections;

        #endregion

        #region Constructor

        public ConnectionManager()
        {
            connections = new List<IScrumPokerClientConnection>();
        }

        #endregion

        #region Properties
        private IList<IScrumPokerClientConnection> Connections
        {
            get
            {
                return connections;
            }
        }

        public bool TimerStarted { get; private set; }
        #endregion

        #region Methods
        public void StartTimer(int seconds)
        {

            if (!TimerStarted)
            {
                foreach (var item in connections)
                {
                    item.StartTimer(seconds);
                }
                TimerStarted = true;
            }
        }

        public void StopTimer()
        {
            if (TimerStarted)
            {
                foreach (var item in connections)
                {
                    item.StopTimer();
                }
                TimerStarted = false;
            }
        }

        public void SelectWorkItem(ITeamWorkItem workItem)
        {
            if (!TimerStarted)
            {
                foreach (var item in connections)
                {
                    item.SelectWorkItem(workItem.Title, workItem.Description);
                }
            }
        }

        public void ResetVote()
        {
            foreach (var item in connections)
            {
                item.ResetVote();
            }
        }

        public void Register(IScrumPokerClientConnection connection)
        {
            Guard.IsNotNull(connection, "connection");
            if (CanRegister(connection))
            {
                Connections.Add(connection);
                connection.Voted += ClientVoted;
                RaiseClientSubscribed(connection.ClientName);
            }
        }

        public void Unregister(IScrumPokerClientConnection connection)
        {
            if (Connections.Contains(connection))
            {
                connection.Voted -= ClientVoted;
                Connections.Remove(connection);
                RaiseClientUnsubscribed(connection.ClientName);
            }
        }

        public bool CanRegister(IScrumPokerClientConnection connection)
        {
            if (connection.IsNotNull())
            {
                var registered = from c in Connections
                                 where string.Compare(c.ClientName, connection.ClientName, StringComparison.OrdinalIgnoreCase) == 0
                                 select c;

                return registered.Count() == 0;
            }
            return false;
        }

        private void ClientVoted(object sender, GenericEventArgs<double> e)
        {
            //TODO: check if the timer has elapsed?
            IScrumPokerClientConnection connection = sender as IScrumPokerClientConnection;
            if (connection.IsNotNull())
            {
                RaiseClientVote(connection.ClientName, e.Data);
            }
        }


        #endregion
    }
}
